package com.fight.strive.sys.modules.dict.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fight.strive.sys.modules.dict.constants.DictConstants;
import com.fight.strive.sys.modules.dict.dao.DictDataMapper;
import com.fight.strive.sys.modules.dict.dto.DictDataDto;
import com.fight.strive.sys.modules.dict.dto.DictDataRequestDto;
import com.fight.strive.sys.modules.dict.entity.DictDataEntity;
import com.fight.strive.sys.modules.dict.service.DictDataService;
import com.fight.strive.sys.modules.exception.StriveException;
import com.fight.strive.sys.modules.mybatisplus.utils.MyBatisPlusUtils;
import com.fight.strive.sys.modules.request.dto.PageRequest;
import com.fight.strive.sys.modules.response.dto.PageData;
import com.fight.strive.sys.modules.sysadmin.utils.SysAdminUtils;
import com.fight.strive.sys.utils.BeanUtils;
import com.fight.strive.sys.utils.CollectionUtils;
import com.fight.strive.sys.utils.ObjectUtils;
import com.fight.strive.sys.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collections;
import java.util.List;

/**
 * @author ZHOUXIANG
 */
@Slf4j
@Service
public class DictDataServiceImpl
        extends ServiceImpl<DictDataMapper, DictDataEntity>
        implements DictDataService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void saveDict(@Valid DictDataDto dictDataDto) {
        // 父级的字典项目的父ID规定为-1
        if (ObjectUtils.isNull(dictDataDto.getParentId())) {
            dictDataDto.setParentId(-1L);
        }
        DictDataEntity dictDataEntity = this.getDictByCodeNotId(
                dictDataDto.getDictCode(), dictDataDto.getId());
        if (ObjectUtils.notNull(dictDataEntity)) {
            throw new StriveException("该字典代码已存在！");
        }
        dictDataEntity = new DictDataEntity();
        BeanUtils.copyPropertiesIgnoreNull(dictDataDto, dictDataEntity);
        // dictCode不同的租户可以重复
        this.saveOrUpdate(dictDataEntity);
        // 存入缓存
        this.updateRedisData(dictDataEntity);
    }

    @Override
    public PageData<DictDataEntity> getDictList(
            PageRequest<DictDataDto> pageRequest) {
        DictDataDto condition = pageRequest.getCondition();
        // 父级的字典项目的父ID规定为-1
        if (ObjectUtils.isNull(condition.getParentId())) {
            condition.setParentId(-1L);
        }
        QueryWrapper<DictDataEntity> queryWrapper = new QueryWrapper<>();
        MyBatisPlusUtils.buildQuery(queryWrapper, condition,
                "dictCode", "parentId");
        MyBatisPlusUtils.buildSortOrderQuery(queryWrapper, pageRequest.getSort());
        Page<DictDataEntity> page = pageRequest.buildMybatisPlusPage();
        IPage<DictDataEntity> iPage = this.page(page, queryWrapper);
        return new PageData<>(iPage);
    }

    @Override
    public void deleteDict(DictDataDto dictDataDto) {
        if (ObjectUtils.isNull(dictDataDto.getId())) {
            throw new StriveException("要删除的字典项ID为空");
        }
        List<DictDataEntity> list =
                this.getDictByParentId(dictDataDto.getId());
        if (CollectionUtils.isNotEmpty(list)) {
            throw new StriveException("字典有子项，不可删除");
        }
        // 删除缓存
        this.deleteRedisData(
                this.getDictById(dictDataDto.getId()));
        // 删除数据库
        this.removeById(dictDataDto.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatchDict(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new StriveException("要删除的字典项ID为空");
        }
        // 删除缓存
        for (Long id : ids) {
            List<DictDataEntity> list = this.getDictByParentId(id);
            if (CollectionUtils.isNotEmpty(list)) {
                throw new StriveException("字典有子项，不可删除");
            }
            this.deleteRedisData(this.getDictById(id));
        }
        // 删除数据库
        this.removeByIds(ids);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<DictDataEntity> getDictByParentId(Long parentId) {
        if (ObjectUtils.isNull(parentId)) {
            throw new StriveException("父级字典项ID为空");
        }
        List<DictDataEntity> list =
                (List<DictDataEntity>) redisTemplate.opsForHash().get(
                        DictConstants.PARENT_ID_KEY, parentId);
        if (ObjectUtils.isNull(list)) {
            QueryWrapper<DictDataEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("PARENT_ID", parentId);
            list = this.list(queryWrapper);
            // 存入缓存, 空的也缓存
            redisTemplate.opsForHash().put(
                    DictConstants.PARENT_ID_KEY, parentId, list);
        }
        return list;
    }

    @Override
    public List<DictDataEntity> getDictByParentCode(String parentCode) {
        if (ObjectUtils.isNull(parentCode)) {
            throw new StriveException("父级字典项代码为空");
        }
        Long parentId = this.getIdByCode(parentCode);
        if (ObjectUtils.notNull(parentId)) {
            // 从缓存中读取
            return this.getDictByParentId(parentId);
        }
        return Collections.emptyList();
    }

    @Override
    public String getDictValueByCode(String code) {
        if (StringUtils.isNotBlank(code)) {
            String value = (String) redisTemplate.opsForHash().get(
                    DictConstants.CODE_VALUE_KEY, code);
            if (ObjectUtils.isNull(value)) {
                DictDataEntity entity = this.getDictByCode(code);
                value = entity.getDictValue();
                // 存入缓存
                redisTemplate.opsForHash().put(
                        DictConstants.CODE_VALUE_KEY, code, value);
            }
            return value;
        }
        return null;
    }

    @Override
    public DictDataEntity getDictByCode(String code) {
        Long tenantId = SysAdminUtils.getCurrentTenantId();
        DictDataEntity entity = this.getRedisData(
                DictConstants.CODE_ID_KEY
                        .concat(String.valueOf(tenantId)), code);
        if (ObjectUtils.isNull(entity)) {
            QueryWrapper<DictDataEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("DICT_CODE", code);
            entity = this.getOne(queryWrapper, false);
            this.updateRedisData(entity);
        }
        return entity;
    }

    @Override
    public DictDataEntity getDictByCodeNotId(String code, Long id) {
        QueryWrapper<DictDataEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("DICT_CODE", code)
                .ne(ObjectUtils.notNull(id), "id", id);
        return this.getOne(queryWrapper, false);
    }

    @Override
    public List<DictDataEntity> getDictByParent(DictDataRequestDto dto) {
        Long parentId = this.getIdByCode(dto.getParentCode());
        QueryWrapper<DictDataEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(ObjectUtils.notNull(parentId), "PARENT_ID", parentId)
                .like(StringUtils.isNotBlank(dto.getDictValue()), "DICT_VALUE", dto.getDictValue())
                .eq(StringUtils.isNotBlank(dto.getAddValue()), "ADD_VALUE", dto.getAddValue());
        queryWrapper.orderByAsc("SEQUENCE");
        return this.list(queryWrapper);
    }

    @Override
    public Long getIdByCode(String code) {
        Long id = (Long) redisTemplate.opsForHash().get(
                DictConstants.CODE_ID_KEY, code);
        if (ObjectUtils.isNull(id)) {
            DictDataEntity dictDataEntity = this.getDictByCode(code);
            if (ObjectUtils.notNull(dictDataEntity)) {
                id = dictDataEntity.getId();
                // 加入缓存
                redisTemplate.opsForHash().put(
                        DictConstants.CODE_ID_KEY, code, id);
            }
        }
        return id;
    }

    @Override
    public DictDataEntity getDictById(Long id) {
        DictDataEntity entity =
                this.getRedisData(DictConstants.DICT_ID_KEY, id);
        if (ObjectUtils.isNull(entity)) {
            entity = this.getById(id);
            if (ObjectUtils.notNull(entity)) {
                this.updateRedisData(entity);
            }
        }
        return entity;
    }

    @Override
    public void updateRedisData(DictDataEntity entity) {
        // dict id -> entity
        redisTemplate.opsForHash().put(
                DictConstants.DICT_ID_KEY, entity.getId(), entity);
        // dict code -> dict id
        redisTemplate.opsForHash().put(
                DictConstants.CODE_ID_KEY.concat(
                        String.valueOf(entity.getTenantId())),
                entity.getDictCode(), entity.getId());
        // code -> value
        redisTemplate.opsForHash().put(
                DictConstants.CODE_VALUE_KEY
                        .concat(String.valueOf(entity.getTenantId())),
                entity.getDictCode(), entity.getDictValue());
        if (entity.getParentId() > -1L) {
            // 删除父组列表缓存
            redisTemplate.opsForHash().delete(
                    DictConstants.PARENT_ID_KEY, entity.getParentId());
        }
    }

    @Override
    public DictDataEntity getRedisData(String key, Object hashKey) {
        if (DictConstants.DICT_ID_KEY.equals(key)) {
            return (DictDataEntity) redisTemplate.opsForHash().get(key, hashKey);
        } else {
            Long id = (Long) redisTemplate.opsForHash().get(key, hashKey);
            if (ObjectUtils.notNull(id)) {
                return (DictDataEntity) redisTemplate.opsForHash().get(
                        DictConstants.DICT_ID_KEY, id);
            }
        }
        return null;
    }

    @Override
    public void deleteRedisData(DictDataEntity entity) {
        redisTemplate.opsForHash().delete(
                DictConstants.DICT_ID_KEY, entity.getId());
        redisTemplate.opsForHash().delete(
                DictConstants.CODE_ID_KEY.concat(
                        String.valueOf(entity.getTenantId())),
                entity.getDictCode());
        redisTemplate.opsForHash().delete(
                DictConstants.CODE_VALUE_KEY.concat(
                        String.valueOf(entity.getTenantId())),
                entity.getDictCode());
        redisTemplate.opsForHash().delete(
                DictConstants.PARENT_ID_KEY, entity.getId());
    }
}
